﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Diagnostics;
using System.Threading;
using ArgusLib.Math;
using ArgusLib.Collections;

namespace ArgusLib.Fractals.OrbitFractals
{
	public class DataSet<T> : IDataSetIterations
		where T:struct
	{
		Reference<T>[,] zValues;
		int[,] iterations;
		bool[,] isFinished;
		int iterLower;
		int iterUpper;

		public int DimensionX { get; private set; }
		public int DimensionY { get; private set; }
		public Parameter CalculationParameter { get; private set; }
		IParameter IDataSet.CalculationParameter { get { return this.CalculationParameter; } }
		IParameterIterations IDataSetIterations.CalculationParameter { get { return this.CalculationParameter; } }
		public IndexedPropertyGet<int, int, int> Iterations { get; private set; }
		public Int32Interval IntervalIterations { get; private set; }
		public IndexedPropertyGet<bool, int, int> IsNull { get; private set; }
		public IndexedProperty<bool, int, int> IsFinished { get; private set; }
		public string DumpFile { get; set; }

		public DataSet()
		{
		}

		private void timerCallback(object arg)
		{
		}

		public virtual void Initialize(Parameter parameter)
		{
			this.DimensionX = parameter.PixelWidth;
			this.DimensionY = parameter.PixelHeight;
			this.CalculationParameter = parameter;
			this.zValues = new Reference<T>[this.DimensionX, this.DimensionY];
			this.iterations = new int[this.DimensionX, this.DimensionY];
			this.isFinished = new bool[this.DimensionX, this.DimensionY];
			this.iterLower = int.MaxValue;
			this.iterUpper = 0;

			this.Iterations = new IndexedPropertyGet<int, int, int>(
				new IndexedPropertyGet<int, int, int>.GetHandler(this.get_Iterations));
			this.IsNull = new IndexedPropertyGet<bool, int, int>(
				new IndexedPropertyGet<bool, int, int>.GetHandler(this.get_IsNull));

			this.IsFinished = new IndexedProperty<bool, int, int>(
				new IndexedProperty<bool, int, int>.GetHandler(this.get_IsFinished),
				new IndexedProperty<bool, int, int>.SetHandler(this.set_IsFinished));

			if (this.DumpFile != null && System.IO.File.Exists(this.DumpFile) == true)
			{
				System.IO.File.WriteAllBytes(this.DumpFile, new byte[0]);
			}
		}

		void IDataSet.Initialize(IParameter parameter)
		{
			this.Initialize((Parameter)parameter);
		}

		public T this[int x, int y]
		{
			get { return this.zValues[x, y].Value; }
			set
			{
				if (this.zValues[x, y] == null)
				{
					this.zValues[x, y] = new Reference<T>(value);
				}
				else
				{
					T oldValue = this.zValues[x, y].Value;
					this.zValues[x, y].Value = value;
					this.OnZValueChanged(x, y, oldValue, value);
				}
			}
		}

		object IDataSet.this[int x, int y]
		{
			get
			{
				if (this.zValues[x, y] == null)
					return null;
				else
					return this.zValues[x, y].Value;
			}
			set
			{
				//if (value is T)
					this[x, y] = (T)value;
			}
		}

		public virtual void PostProcess()
		{
			this.IntervalIterations = new Int32Interval(this.iterLower, this.iterUpper);
		}

		protected virtual void OnZValueChanged(int x, int y, T oldValue, T newValue)
		{
			this.iterations[x, y]++;
		}

		private int get_Iterations(int x, int y) { return this.iterations[x, y]; }

		private bool get_IsNull(int x, int y) { return (this.zValues[x, y] == null); }

		private bool get_IsFinished(int x, int y) { return this.isFinished[x, y]; }

		protected virtual void set_IsFinished(int x, int y, bool value)
		{
			this.isFinished[x, y] = value;
			int iter = this.Iterations[x, y];
			if (iter < this.iterLower)
				this.iterLower = iter;
			if (iter > this.iterUpper)
				this.iterUpper = iter;
		}

		public virtual float GetMemoryUsage(Parameter parameter)
		{
			int sizeOfT = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
			double bytes = (double)(IntPtr.Size + sizeOfT + 4 + 1) * parameter.PixelWidth * parameter.PixelHeight;
			return (float)(bytes / (1 << 20));
		}

		float IDataSet.GetMemoryUsage(IParameter parameter)
		{
			return this.GetMemoryUsage((Parameter)parameter);
		}

		public virtual float GetMemoryUsage()
		{
			return 0f;
		}
	}
}
